﻿using System;
using System.Data.Odbc;

namespace Centido.Core
{
    /// <summary>
    ///   A set of table column update operations.
    /// </summary>
    public static class ColumnUpdateOperations
    {
        /// <summary>
        ///   Updates data in the column of the specified row in the table.
        /// </summary>
        /// <param name="column">The target table column.</param>
        /// <param name = "rowId">The id of the specified row.</param>
        /// <param name="expectedRowVersion">The expected version of the row.</param>
        /// <param name="dataSource">The target data source.</param>
        /// <param name = "value">The new data.</param>
        /// <returns>Returns true if a row with the specified id was found; otherwise, false.</returns>
        private static bool UpdateImplementation(Column column,
            Guid rowId, Guid? expectedRowVersion, DataSource dataSource,
            object value)
        {
            var tableName = column.GetDataSourceName(dataSource);

            return column.Domain.DbAccess.ExecuteNonQuery(
                cmd =>
                {
                    cmd.CommandText = string.Format(
                        "update {0} set {1}=? where {2}=?",
                        tableName,
                        column.Name,
                        Db.Tables.UserTable.Col.Id);

                    // If the expected row version was specified
                    if (expectedRowVersion.HasValue)
                        cmd.CommandText += string.Format(" and {0}=?",
                            Db.Tables.UserTable.Col.RowVersion);

                    cmd.Parameters.Add(column.Domain.DbAccess.CreateParameter("value", 
                        column.ValueOdbcType, DbValue.GetDbValue(value)));
                    cmd.Parameters.Add(column.Domain.DbAccess.CreateParameter("id",
                        OdbcType.Binary, rowId.ToByteArray()));

                    // If the expected row version was specified
                    if (expectedRowVersion.HasValue)
                        cmd.Parameters.Add(column.Domain.DbAccess.CreateParameter(
                            "row_version", OdbcType.Binary,
                            expectedRowVersion.Value.ToByteArray()));
                }) != 0;
        }


        /// <summary>
        ///   Updates data in the column of a row with the specified id and version in the table.
        /// </summary>
        /// <remarks>
        /// <para>Updating specifying the version of the row guarantees
        /// that the row has not changed since the last selection.</para>
        /// <para> The current user has to have the update permission on the table in order to execute this function
        /// if the data source is the user view of the table. And the current user has to have the permission
        /// to change the schema if the data source is the internal table.</para>
        /// </remarks>
        /// <param name="column">The target table column. </param>
        /// <param name="rowId">The id of the row.</param>
        /// <param name="expectedRowVersion">The expected version of the row.</param>
        /// <param name="dataSource">The target data source.</param>
        /// <param name="value">The new data.</param>
        /// <exception cref="System.Data.Common.DbException">Thrown when the user has no permission to execute this function.</exception>
        /// <returns>Returns true if a row with the specified id and version was found; otherwise, false.</returns>
        /// <example>
        ///   The following example shows how to update data in the column specifying the id and the version of the row.
        /// <code>
        ///     using System;
        ///     using Centido.Core;
        /// 
        ///     class Sample
        ///     {
        ///         static void Main()
        ///         {
        ///             var domainConfig = new DomainConfiguration()
        ///             {
        ///                 DbConnectionString = @"Driver={SQL Server};Server=.\SQLEXPRESS;Database=MyDb;Uid=admin;Pwd=P@ssw0rd;",
        ///                 DbProviderName = "MSSQLSERVER"
        ///             };
        ///         
        ///             var domain = Domain.Build(domainConfig);
        ///         
        ///             domain.DeleteAll();
        ///             
        ///             //Adding a new table
        ///             var table = domain.AddTable("my_table");
        /// 
        ///             //Adding a column
        ///             var column = table.AddColumn("my_column", ColumnType.String);
        /// 
        ///             //Granting all necessary permissions
        ///             table.GrantAll();
        /// 
        ///             //Inserting a row
        ///             var id = table.InsertRow(column.Value("My value"));
        /// 
        ///             //Reading the row version
        ///             var rowVersion = table.SystemColumns.RowVersion.Read(id).GetGuid().Value;
        /// 
        ///             //Updating the column specifying the row version
        ///             var updateResult = column.Update(id, rowVersion, DataSource.UserView, "My new value");
        /// 
        ///             Console.WriteLine("The first update: result = {0}", updateResult);
        ///             Console.WriteLine("The column: value = {0}", column.Read(id).GetString());
        /// 
        ///             //Updating the column specifying the outdated row version
        ///            updateResult = column.Update(id, rowVersion, DataSource.UserView, "Another value");
        /// 
        ///             Console.WriteLine("The first update: result = {0}", updateResult);
        ///             Console.WriteLine("The column: value = {0}", column.Read(id).GetString());
        ///         }
        ///     }
        /// </code>
        /// </example>
        public static bool Update(this Column column,
            Guid rowId, Guid? expectedRowVersion, DataSource dataSource, object value)
        {
            return UpdateImplementation(column, rowId, expectedRowVersion, dataSource, value);
        }


        /// <summary>
        ///   Updates data in the column of the specified row.
        /// </summary>
        /// <remarks>
        /// The current user has to have the update permission on the table in order to execute this function.
        /// </remarks>
        /// <param name="column">The target table column.</param>
        /// <param name = "rowId">The id of the specified row.</param>
        /// <param name = "value">The new data.</param>
        /// <returns>Returns true if a row with the specified id was found; otherwise, false.</returns>
        /// <exception cref="System.Data.Common.DbException">Thrown when the user has no permission to execute this function.</exception>
        /// <example>
        ///   The following example shows how to update data inside the column.
        /// <code>
        ///     using System;
        ///     using Centido.Core;
        /// 
        ///     class Sample
        ///     {
        ///         static void Main()
        ///         {
        ///             var domainConfig = new DomainConfiguration()
        ///             {
        ///                 DbConnectionString = @"Driver={SQL Server};Server=.\SQLEXPRESS;Database=MyDb;Uid=admin;Pwd=P@ssw0rd;",
        ///                 DbProviderName = "MSSQLSERVER"
        ///             };
        ///         
        ///             var domain = Domain.Build(domainConfig);
        ///         
        ///             domain.DeleteAll();
        ///             
        ///             //Adding a new table
        ///             var table = domain.AddTable("my_table");
        /// 
        ///             //Adding two columns
        ///             var firstColumn = table.AddColumn("first_column", ColumnType.String);
        ///             var secondColumn = table.AddColumn("second_column", ColumnType.Integer);
        /// 
        ///             //Granting all necessary permissions
        ///             table.GrantAll();
        /// 
        ///             //Inserting a row
        ///             var id = table.InsertRow(firstColumn.Value("My value"), secondColumn.Value(1));
        /// 
        ///             //Reading inserted values
        ///             Console.WriteLine("Inserted row: first column = {0}, second column = {1}",
        ///                 firstColumn.Read(id).GetString(),
        ///                 secondColumn.Read(id).GetInteger());
        /// 
        ///             //Updating the row
        ///             firstColumn.Update(id, "The updated value");
        ///             secondColumn.Update(id, 2);
        /// 
        ///             //Reading updated values
        ///             Console.WriteLine("Updated row: first column = {0}, second column = {1}",
        ///                 firstColumn.Read(id).GetString(),
        ///                 secondColumn.Read(id).GetInteger());
        ///         }
        ///     }
        /// </code>
        /// </example>
        public static bool Update(this Column column, Guid rowId, object value)
        {
            return Update(column, rowId, null, DataSource.UserView, value);
        }


        /// <summary>
        ///   Updates data in the column of the specified row.
        /// </summary>
        /// <remarks>
        ///   The current user has to have the update permission on the table
        ///  in order to execute this function.
        /// </remarks>
        /// <param name="column">The target table column.</param>
        /// <param name="rowId">The id of the specified row.</param>
        /// <param name="dataSource"></param>
        /// <param name="value">The column new data.</param>
        /// <returns>Returns true if a row with the specified id; otherwise, false.</returns>
        /// <exception cref="System.Data.Common.DbException">Thrown when the user has no permission to execute this function.</exception>
        /// <example>
        ///   The following example shows how to update data inside a table column.
        /// <code>
        ///     using System;
        ///     using Centido.Core;
        /// 
        ///     class Sample
        ///     {
        ///         static void Main()
        ///         {
        ///             var domainConfig = new DomainConfiguration()
        ///             {
        ///                 DbConnectionString = @"Driver={SQL Server};Server=.\SQLEXPRESS;Database=MyDb;Uid=admin;Pwd=P@ssw0rd;",
        ///                 DbProviderName = "MSSQLSERVER"
        ///             };
        ///         
        ///             var domain = Domain.Build(domainConfig);
        ///         
        ///             domain.DeleteAll();
        ///             
        ///             //Adding a new table
        ///             var table = domain.AddTable("my_table");
        /// 
        ///             //Adding two columns
        ///             var firstColumn = table.AddColumn("first_column", ColumnType.String);
        ///             var secondColumn = table.AddColumn("second_column", ColumnType.Integer);
        /// 
        ///             //Granting all necessary permissions
        ///             table.GrantAll();
        /// 
        ///             //Inserting a row
        ///             var id = table.InsertRow(firstColumn.Value("My value"), secondColumn.Value(1));
        /// 
        ///             //Reading inserted values
        ///             Console.WriteLine("Inserted row: first column = {0}, second column = {1}",
        ///                 firstColumn.Read(id).GetString(),
        ///                 secondColumn.Read(id).GetInteger());
        /// 
        ///             //Updating the row
        ///             firstColumn.Update(id, DataSource.UserView, "The updated value");
        ///             secondColumn.Update(id, DataSource.UserView, 2);
        /// 
        ///             //Reading updated values
        ///             Console.WriteLine("Updated row: first column = {0}, second column = {1}",
        ///                 firstColumn.Read(id).GetString(),
        ///                 secondColumn.Read(id).GetInteger());
        ///         }
        ///     }
        /// </code>
        /// </example>
        public static bool Update(this Column column, Guid rowId,
            DataSource dataSource, object value)
        {
            if (column == null)
                throw new ArgumentNullException("column");

            return Update(column, rowId, null, dataSource, value);
        }


        /// <summary>
        ///   Updates data in the column of the specified row.
        /// </summary>
        /// <remarks>
        /// <para>Updating specifying the version of the row guarantees
        /// that the row has not changed since the last selection.</para>
        /// <para>
        ///   The current user has to have the update permission on the table
        ///  in order to execute this function.
        /// </para>
        /// </remarks>
        /// <param name="column">The target table column.</param>
        /// <param name="rowId">The id of the specified row.</param>
        /// <param name="expectedRowVersion">The expected version of the row.</param>
        /// <param name="value">The new column data.</param>
        /// <returns>Returns true if a row with the specified id and version was found; otherwise, false.</returns>
        /// <exception cref="System.Data.Common.DbException">Thrown when the user has no permission to execute this function.</exception>
        /// <example>
        ///   The following example shows how to update data in the column specifying the id and the version of the row.
        /// <code>
        ///     using System;
        ///     using Centido.Core;
        /// 
        ///     class Sample
        ///     {
        ///         static void Main()
        ///         {
        ///             var domainConfig = new DomainConfiguration()
        ///             {
        ///                 DbConnectionString = @"Driver={SQL Server};Server=.\SQLEXPRESS;Database=MyDb;Uid=admin;Pwd=P@ssw0rd;",
        ///                 DbProviderName = "MSSQLSERVER"
        ///             };
        ///         
        ///             var domain = Domain.Build(domainConfig);
        ///         
        ///             domain.DeleteAll();
        ///             
        ///             //Adding a new table
        ///             var table = domain.AddTable("my_table");
        /// 
        ///             //Adding a column
        ///             var column = table.AddColumn("my_column", ColumnType.String);
        /// 
        ///             //Granting all necessary permissions
        ///             table.GrantAll();
        /// 
        ///             //Inserting a row
        ///             var id = table.InsertRow(column.Value("My value"));
        /// 
        ///             //Reading the row version
        ///             var rowVersion = table.SystemColumns.RowVersion.Read(id).GetGuid().Value;
        /// 
        ///             //Updating the column specifying the row version
        ///             var updateResult = column.Update(id, rowVersion, "My new value");
        /// 
        ///             Console.WriteLine("The first update: result = {0}", updateResult);
        ///             Console.WriteLine("The column: value = {0}", column.Read(id).GetString());
        /// 
        ///             //Updating the column specifying the outdated row version
        ///             updateResult = column.Update(id, rowVersion, "Another value");
        /// 
        ///             Console.WriteLine("The first update: result = {0}", updateResult);
        ///             Console.WriteLine("The column: value = {0}", column.Read(id).GetString());
        ///         }
        ///     }
        /// </code>
        /// </example>
        public static bool Update(this Column column, Guid rowId,
            Guid? expectedRowVersion, object value)
        {
            return Update(column, rowId, expectedRowVersion, DataSource.UserView, value);
        }
    }
}
